Skip to content

Fix creating SpecFlow compat package (v3.x)#1070

Open
304NotModified wants to merge 4 commits into
product-lines/v3from
fix-specflow-compat.v3.3.4
Open

Fix creating SpecFlow compat package (v3.x)#1070
304NotModified wants to merge 4 commits into
product-lines/v3from
fix-specflow-compat.v3.3.4

Conversation

@304NotModified
Copy link
Copy Markdown
Member

@304NotModified 304NotModified commented Mar 26, 2026

🤔 What's changed?

Fix the SpecFlow compat package

ℹ️ Please note, this isn't targeting main as this is a fix for 3.x. Main is already 4.x. See #1068 (comment)

Build:
https://github.com/reqnroll/Reqnroll/actions/runs/23622833511 (manual as this isn't a PR to main)

⚡️ What's your motivation?

#1068

🏷️ What kind of change is this?

  • 🏦 Refactoring/debt/DX (improvement to code design, tooling, etc. without changing behaviour)

@304NotModified 304NotModified changed the title IsPackable to true for SpecFlow compat package IsPackable to true for SpecFlow compat package (v3.x) Mar 26, 2026
@304NotModified 304NotModified changed the title IsPackable to true for SpecFlow compat package (v3.x) Fix creating SpecFlow compat package (v3.x) Mar 26, 2026
@304NotModified 304NotModified marked this pull request as ready for review March 31, 2026 20:06
@304NotModified
Copy link
Copy Markdown
Member Author

304NotModified commented Mar 31, 2026

I compared the preview package (from https://github.com/reqnroll/Reqnroll/actions/runs/23622833511/artifacts/6134804719) with the latest, 3.3.3.

There has the same amount of files and the packages looks the same to me.

(Left 3.3.4i-ci, right 3.3.3)

image

@gasparnagy gasparnagy changed the base branch from v3.x to product-lines/v3 May 21, 2026 12:31
@gasparnagy gasparnagy force-pushed the fix-specflow-compat.v3.3.4 branch from ac3fa6e to 0f2339c Compare May 21, 2026 12:53
@gasparnagy
Copy link
Copy Markdown
Contributor

gasparnagy commented May 22, 2026

Note to self. The error is:

System.ArgumentNullException: Value cannot be null. (Parameter 'key')
   at System.Collections.Generic.Dictionary`2.TryGetValue(TKey key, TValue& value)
   at NuGet.Build.Tasks.Pack.PackTaskLogic.InitLibFiles(IMSBuildItem[] libFiles, IDictionary`2 aliases)
   at NuGet.Build.Tasks.Pack.PackTaskLogic.GetPackArgs(IPackTaskRequest`1 request)
   at NuGet.Build.Tasks.Pack.PackTask.Execute()

from C:\Program Files\dotnet\sdk\10.0.204\NuGet.Build.Tasks.Pack.dll

The error comes from libFile.GetProperty("TargetFramework") being null
Where libFile is an item from request.BuildOutputInPackage or request.TargetPathsToSymbols
Where request is built as

private IPackTaskRequest<IMSBuildItem> GetRequest()
  {
    return (IPackTaskRequest<IMSBuildItem>) new PackTaskRequest()
    {
      ...
      BuildOutputInPackage = MSBuildUtility.WrapMSBuildItem((IEnumerable<ITaskItem>) this.BuildOutputInPackage),
      TargetPathsToSymbols = MSBuildUtility.WrapMSBuildItem((IEnumerable<ITaskItem>) this.TargetPathsToSymbols),
    };
  }

for BuildOutputInPackage all has TargetFramework
for TargetPathsToSymbols most has, except this two:

W:\Reqnroll\Reqnroll\Plugins\Reqnroll.SpecFlowCompatibility\bin\Debug\net462\Reqnroll.SpecFlowCompatibility.pdb
    ReferenceSourceTarget = ProjectReference
    MSBuildSourceProjectFile = W:\Reqnroll\Reqnroll\Plugins\Reqnroll.SpecFlowCompatibility\Reqnroll.SpecFlowCompatibility.csproj
    HasSingleTargetFramework = false
    SetTargetFramework = TargetFramework=net462
    TargetFrameworkIdentifier = .NETFramework
    SetPlatform = Platform=AnyCPU
    FullConfiguration = Debug|AnyCPU
    AdditionalProperties = TargetFramework=net462
    PackAsComponent = true
    ReferenceOutputAssembly = true
    IsVcxOrNativeProj = false
    Version = 3.0.0.0
    OutputItemType = 
    TargetPlatformMonikers = Windows,Version=7.0;Windows,Version=7.0
    TargetPlatformMoniker = Windows,Version=7.0
    ProjectReferenceOriginalItemSpec = ..\Reqnroll.SpecFlowCompatibility\Reqnroll.SpecFlowCompatibility.csproj
    BuildReference = true
    Platform = AnyCPU
    CopyUpToDateMarker = W:\Reqnroll\Reqnroll\Plugins\Reqnroll.SpecFlowCompatibility\obj\Debug\net462\Reqnroll.FB38480E.Up2Date
    AdditionalPropertiesFromProject = <AdditionalProjectProperties>
  <TargetFramework Name="netstandard2.0">
    <SelfContained></SelfContained>
    <_IsExecutable></_IsExecutable>
    <IsRidAgnostic>true</IsRidAgnostic>
    <ShouldBeValidatedAsExecutableReference></ShouldBeValidatedAsExecutableReference>
    <_SelfContainedWasSpecified></_SelfContainedWasSpecified>
  </TargetFramework>
  <TargetFramework Name="net462">
    <SelfContained></SelfContained>
    <_IsExecutable></_IsExecutable>
    <IsRidAgnostic>true</IsRidAgnostic>
    <ShouldBeValidatedAsExecutableReference></ShouldBeValidatedAsExecutableReference>
    <_SelfContainedWasSpecified></_SelfContainedWasSpecified>
  </TargetFramework>
</AdditionalProjectProperties>
    TargetFrameworkMonikers = .NETStandard,Version=v2.0;.NETFramework,Version=v4.6.2
    TargetFrameworks = netstandard2.0;net462
    PrivateAssets = all
    Configuration = Debug
    IsRidAgnostic = true
    MSBuildSourceTargetName = GetTargetFrameworks
    ResolvedFrom = W:\Reqnroll\Reqnroll\Plugins\Reqnroll.SpecFlowCompatibility\bin\Debug\net462\Reqnroll.SpecFlowCompatibility.dll
    SetConfiguration = Configuration=Debug
    NearestTargetFramework = net462
    UndefineProperties = ;RuntimeIdentifier;SelfContained
    OriginalProjectReferenceItemSpec = ..\Reqnroll.SpecFlowCompatibility\Reqnroll.SpecFlowCompatibility.csproj
    TargetPlatformIdentifier = Windows
    TargetFrameworkVersion = 4.6.2
    Platforms = AnyCPU
    Targets = 
    CopyLocal = true
    OriginalItemSpec = W:\Reqnroll\Reqnroll\Plugins\Reqnroll.SpecFlowCompatibility\bin\Debug\net462\Reqnroll.SpecFlowCompatibility.dll
    Asset = W:\Reqnroll\Reqnroll\Plugins\Reqnroll.SpecFlowCompatibility\bin\Debug\net462\Reqnroll.SpecFlowCompatibility.pdb
W:\Reqnroll\Reqnroll\Plugins\Reqnroll.SpecFlowCompatibility\bin\Debug\netstandard2.0\Reqnroll.SpecFlowCompatibility.pdb
    ReferenceSourceTarget = ProjectReference
    MSBuildSourceProjectFile = W:\Reqnroll\Reqnroll\Plugins\Reqnroll.SpecFlowCompatibility\Reqnroll.SpecFlowCompatibility.csproj
    HasSingleTargetFramework = false
    SetTargetFramework = TargetFramework=netstandard2.0
    TargetFrameworkIdentifier = .NETStandard
    SetPlatform = Platform=AnyCPU
    FullConfiguration = Debug|AnyCPU
    AdditionalProperties = TargetFramework=netstandard2.0
    PackAsComponent = true
    ReferenceOutputAssembly = true
    IsVcxOrNativeProj = false
    Version = 
    OutputItemType = 
    TargetPlatformMonikers = Windows,Version=7.0;Windows,Version=7.0
    TargetPlatformMoniker = Windows,Version=7.0
    ProjectReferenceOriginalItemSpec = ..\Reqnroll.SpecFlowCompatibility\Reqnroll.SpecFlowCompatibility.csproj
    BuildReference = true
    Platform = AnyCPU
    CopyUpToDateMarker = W:\Reqnroll\Reqnroll\Plugins\Reqnroll.SpecFlowCompatibility\obj\Debug\netstandard2.0\Reqnroll.FB38480E.Up2Date
    AdditionalPropertiesFromProject = <AdditionalProjectProperties>
  <TargetFramework Name="netstandard2.0">
    <SelfContained></SelfContained>
    <_IsExecutable></_IsExecutable>
    <IsRidAgnostic>true</IsRidAgnostic>
    <ShouldBeValidatedAsExecutableReference></ShouldBeValidatedAsExecutableReference>
    <_SelfContainedWasSpecified></_SelfContainedWasSpecified>
  </TargetFramework>
  <TargetFramework Name="net462">
    <SelfContained></SelfContained>
    <_IsExecutable></_IsExecutable>
    <IsRidAgnostic>true</IsRidAgnostic>
    <ShouldBeValidatedAsExecutableReference></ShouldBeValidatedAsExecutableReference>
    <_SelfContainedWasSpecified></_SelfContainedWasSpecified>
  </TargetFramework>
</AdditionalProjectProperties>
    TargetFrameworkMonikers = .NETStandard,Version=v2.0;.NETFramework,Version=v4.6.2
    TargetFrameworks = netstandard2.0;net462
    PrivateAssets = all
    Configuration = Debug
    IsRidAgnostic = true
    MSBuildSourceTargetName = GetTargetFrameworks
    ResolvedFrom = W:\Reqnroll\Reqnroll\Plugins\Reqnroll.SpecFlowCompatibility\bin\Debug\netstandard2.0\Reqnroll.SpecFlowCompatibility.dll
    SetConfiguration = Configuration=Debug
    NearestTargetFramework = netstandard2.0
    UndefineProperties = ;RuntimeIdentifier;SelfContained
    OriginalProjectReferenceItemSpec = ..\Reqnroll.SpecFlowCompatibility\Reqnroll.SpecFlowCompatibility.csproj
    TargetPlatformIdentifier = Windows
    TargetFrameworkVersion = 2.0
    Platforms = AnyCPU
    Targets = 
    CopyLocal = true
    OriginalItemSpec = W:\Reqnroll\Reqnroll\Plugins\Reqnroll.SpecFlowCompatibility\bin\Debug\netstandard2.0\Reqnroll.SpecFlowCompatibility.dll
    Asset = W:\Reqnroll\Reqnroll\Plugins\Reqnroll.SpecFlowCompatibility\bin\Debug\netstandard2.0\Reqnroll.SpecFlowCompatibility.pdb

Interestingly these both have a duplicate as well where the targetframework is properly set...

Somehow the issue roots back to @(ReferenceCopyLocalPaths)...

@gasparnagy
Copy link
Copy Markdown
Contributor

gasparnagy commented May 22, 2026

Update on investigation.

It seems that the target "CollectProjectReferenceDebugSymbolsForPack" that we included in Directory.Build.targets (

<Target Name="CollectProjectReferenceDebugSymbolsForPack" DependsOnTargets="$(CollectProjectReferenceDebugSymbolsForPackDependsOn)">
<ItemGroup>
<TfmSpecificDebugSymbolsFile
Include="@(_ProjectReferenceCopyLocalPathsForPackAsComponent)"
Condition="'%(Extension)' == '.pdb'"/>
</ItemGroup>
<ItemGroup>
<_TfmSpecificDebugSymbolsCandidate
Include="@(_ProjectReferenceCopyLocalPathsForPackAsComponent->'%(RootDir)%(Directory)%(Filename).pdb')"
Condition="'%(Extension)' != '.pdb'"/>
<TfmSpecificDebugSymbolsFile
Include="@(_TfmSpecificDebugSymbolsCandidate)"
TargetFramework="$(TargetFramework)"
Condition="Exists('%(FullPath)')" />
</ItemGroup>
</Target>
) is not compatible somehow with this package.

But there are some generic issues as well. For example the target should include any non-pdb file to _TfmSpecificDebugSymbolsCandidate:

      <_TfmSpecificDebugSymbolsCandidate
        Include="@(_ProjectReferenceCopyLocalPathsForPackAsComponent->'%(RootDir)%(Directory)%(Filename).pdb')"
        Condition="'%(Extension)' != '.pdb'"/>

However in reality, the .pdb files are also included there:

image

Funnily this is not what causes this particular issue but the other include:

      <TfmSpecificDebugSymbolsFile
        Include="@(_ProjectReferenceCopyLocalPathsForPackAsComponent)"
        Condition="'%(Extension)' == '.pdb'"/>

because this causes the pdb node to be included without a target framework, which at the end causing the error.

I keep investigating but if @Code-Grump has any memories about this target, please share.

@gasparnagy
Copy link
Copy Markdown
Contributor

I think I have a better picture now.

The target CollectProjectReferenceDebugSymbolsForPack adds items to TfmSpecificDebugSymbolsFile in two places:

<TfmSpecificDebugSymbolsFile
Include="@(_ProjectReferenceCopyLocalPathsForPackAsComponent)"
Condition="'%(Extension)' == '.pdb'"/>

This suppose to add the PDB files for cases when _ProjectReferenceCopyLocalPathsForPackAsComponent contains a .pdb. In reality it is only effective for the Reqnroll.SpecFlowCompatibility.ReqnrollPlugin project. Because of #1058 this project was not packaged so the result was not used. So basically so far it was a dead code and this is why we did not face this issue.

<_TfmSpecificDebugSymbolsCandidate
Include="@(_ProjectReferenceCopyLocalPathsForPackAsComponent->'%(RootDir)%(Directory)%(Filename).pdb')"
Condition="'%(Extension)' != '.pdb'"/>
<TfmSpecificDebugSymbolsFile
Include="@(_TfmSpecificDebugSymbolsCandidate)"
TargetFramework="$(TargetFramework)"
Condition="Exists('%(FullPath)')" />

This is adding .pdb for the cases when _ProjectReferenceCopyLocalPathsForPackAsComponent contains a .dll.

This was used by two projects: Reqnroll and Reqnroll.SpecFlowCompatibility.ReqnrollPlugin. The interesting is the Reqnroll, because that was needed even without the SpecFlowCompatibility plugin. It was used to include the pdb file of Reqnroll.Utils and it effectively did. So despite the condition, this was the one that included the Reqnroll.Utils.pdb file to the resulting packages.


Note that the second one sets the TargetFramework property, but the first one does not. I don't know if that was intended, but if I add a TargetFramework="$(TargetFramework)" setting to the first one, the solution compiles again and the SpecFlowCompatibility plugin package is generated and the related source package includes the Reqnroll.SpecFlowCompatibility.pdb and the Reqnroll.SpecFlowCompatibility.ReqnrollPlugin.pdb files. So I will push a commit with this.

Please @Code-Grump have a look and comment if you are fine with this change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants